const str = dispatch(invoke('toString', Array.prototype.toString));
console.log(str([1, 2, 3, 4, 5, 6]));

const rev = dispatch(invoke('reverse', Array.prototype.reverse), stringReverse);
console.log(rev('123'));
console.log(rev(1));

const sillyReverse = dispatch(rev, always(43));
console.log(sillyReverse(10000));

const performCommand = dispatch(
    isa('notify', (obj) => notify(obj.message)),
    isa('join', (obj) => changeView(obj.target))
)

console.log(performCommand({type: 'notify', message: '消息发送成功！'}));

function isa(type, action) {
    return (obj) => {
        if (obj.type === type) {
            return action(obj);
        }
    };
}

function notify(obj) {
    return obj;
}

function changeView(obj) {
    return obj;
}

function always(value) {
    return () => {
        return value;
    };
}

function existence(n) {
    return n !== false && n != null;
}

function dispatch(...fns) {
    const size = fns.length;

    return (obj, ...args) => {
        let ret = undefined;

        for (let i = 0; i < size; i++) {
            const fn = fns[i];
            ret = fn(obj, ...args);
            if (existence(ret)) {
                return ret;
            }
        }

        return ret;
    };
}

function fail(message) {
    return ['FAIL:', message].join(' ');
}

function invoke(str, method) {
    return (target, ...args) => {
        if (!existence(target)) {
            fail('Must provide a target');
            return;
        }

        const targetMethod = target[str]
        if (existence(targetMethod) && targetMethod === method) {
            return targetMethod.apply(target, args);
        }

        return;
    };
}

function stringReverse(s) {
    if (typeof s !== 'string') return;
    return s.split('').reverse().join('');
}